import React, { useState, useEffect } from 'react';
import { Upload, Button, Icon, message, notification, Typography, Modal } from 'antd';
import { getFileType } from '@/pages/EntryManage/utils';
import cookie from 'js-cookie';
import styles from './index.less';

const { Text } = Typography;

const codeMessage = {
    200: '服务器成功返回请求的数据。',
    201: '新建或修改数据成功。',
    202: '一个请求已经进入后台排队（异步任务）。',
    204: '删除数据成功。',
    400: '发出的请求有错误，服务器没有进行新建或修改数据的操作。',
    401: '用户没有权限（令牌、用户名、密码错误）。',
    403: '用户得到授权，但是访问是被禁止的。',
    404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除，且不会再得到的。',
    422: '当创建一个对象时，发生一个验证错误。',
    500: '服务器发生错误，请检查服务器。',
    502: '网关错误。',
    503: '服务不可用，服务器暂时过载或维护。',
    504: '网关超时。'
};

const checkStatus = response => {
    if (response.status >= 200 && response.status < 300) {
        return response;
    }
    const errortext = codeMessage[response.status] || response.statusText;
    notification.error({
        message: `请求错误 ${response.status}: ${response.url}`,
        description: errortext
    });
    const error = new Error(errortext);
    error.name = response.status;
    error.response = response;
    throw error;
};

/**
 * 原生ajax上传，用以显示上传进度
 * @param url 上传路径
 * @param opts 上传配置项
 * @param progressOptions 上传文件的进度配置
 */
const fetch = (url, opts = {}, progressOptions = null) =>
    new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open(opts.method || 'GET', url);
        Object.keys(opts.headers || {}).forEach(key => {
            xhr.setRequestHeader(key, opts.headers[key]);
        });
        xhr.onload = e => {
            const {
                target: { responseText, status }
            } = e;
            resolve({
                text: () => responseText,
                json: () => JSON.parse(responseText) || {},
                status
            });
        };
        xhr.onerror = reject;
        if (xhr.upload && progressOptions) {
            const { file, onProgress } = progressOptions;
            xhr.upload.onprogress = e => {
                const { loaded, total } = e;
                onProgress({ percent: Math.round((loaded / total) * 100) }, file);
            };
        }
        xhr.send(opts.body);
    });

const request = (url, option) => {
    const defaultOptions = {
        credentials: 'include'
    };
    const newOptions = { ...defaultOptions, ...option };
    const token = cookie.get('Authorization');
    if (newOptions.method === 'POST' || newOptions.method === 'PUT' || newOptions.method === 'DELETE') {
        if (!(newOptions.body instanceof FormData)) {
            newOptions.headers = {
                Accept: 'application/json',
                'Content-Type': 'application/json; charset=utf-8',
                ...newOptions.headers,
                Authorization: token
            };
            newOptions.body = JSON.stringify(newOptions.body);
        } else {
            // newOptions.body is FormData
            newOptions.headers = {
                Accept: 'application/json',
                ...newOptions.headers,
                Authorization: token
            };
        }
    }
    return fetch(url, newOptions)
        .then(checkStatus)
        .then(response => {
            // DELETE and 204 do not return data by default
            // using .json will report an error.
            if (newOptions.method === 'DELETE' || response.status === 204) {
                return response.text();
            }
            return response.json();
        });
};

// 上传至服务器
const uploadToServer = params => {
    // const suffix = params?.file?.name
    //     ?.split('.')
    //     ?.reverse()[0]
    //     ?.toLowerCase();

    const defaultOptions = {
        method: 'PUT',
        headers: {
            // 'Content-Type': suffix === 'pdf' ? 'application/pdf' : 'application/octet-stream'
            'Content-Type': 'application/octet-stream'
        }
    };
    defaultOptions.body = params.file;

    // 上传进度
    const progressOptions = {
        file: params.file,
        onProgress: params.onProgress
    };

    return fetch(params.preUrl, defaultOptions, progressOptions)
        .then(checkStatus)
        .then(response => response)
        .catch(e => e);
};

const getBase64 = file =>
    new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = () => resolve(reader.result);
        reader.onerror = error => reject(error);
    });

const OssUpload = (
    {
        value,
        dragAble = false,
        checkFileTypeMsg = null,
        checkFileSizeMsg = null,
        checkFileSize = 0,
        checkFileType = '',
        sysCode = 'WorkFlow',
        businessCode = 'Annex',
        categoryCode = 'annexFile',
        onChange,
        children,
        maxCount = 10,
        isOutFileData = null,
        help = null,
        listType = 'text',
        describe,
        ...rest
    },
    ref
) => {
    const [fileList, setFileList] = useState(value);
    const [count, setCount] = useState(value?.length || 0);

    // 图片预览功能
    const [previewVisible, setPreviewVisible] = useState(false);
    const [previewImage, setPreviewImage] = useState(null);
    // 预览图片
    const handlePreview = async file => {
        if (!file.url && !file.preview) {
            file.preview = await getBase64(file.originFileObj);
        }
        setPreviewVisible(true);
        setPreviewImage(file.url || file.preview);
    };
    const handlePreviewCancel = () => setPreviewVisible(false);
    // 校验上传文件类型
    const checkUpload = (file, filelist) => {
        if (fileList?.length >= maxCount || filelist?.length > maxCount) {
            message.error(`上传不能超过 ${maxCount} 个文件`);
        }
        if (checkFileType?.length > 0) {
            const fileType = file.name.substring(file.name.lastIndexOf('.') + 1);
            if (!checkFileType.toLocaleLowerCase().includes(fileType.toLocaleLowerCase())) {
                if (checkFileTypeMsg) {
                    message.error(checkFileTypeMsg);
                } else {
                    message.error(`只能上传 ${checkFileType}格式的文件`);
                }

                filelist.splice(filelist.indexOf(file), 1);
                return false;
            }
        }

        if (checkFileSize > 0) {
            const isLess = file.size / 1024 / 1024 < checkFileSize;
            if (!isLess) {
                if (checkFileSizeMsg) {
                    message.error(checkFileSizeMsg);
                } else {
                    message.error(`文件不能超过 ${checkFileSize}MB!`);
                }

                filelist.splice(filelist.indexOf(file), 1);
                return false;
            }
        }
        return true;
    };

    // 自定义上传
    const customRequest = async ({ file, onError, onSuccess, onProgress }) => {
        // 获取上传地址
        const { code, msg, data } = await request('/api/resource/public/oss/producePutPreSignUrls', {
            method: 'POST',
            body: {
                sysCode,
                businessCode,
                categoryCode,
                fileNames: [file.name],
                file
            }
        });
        if (code === 10000) {
            const { keyName, preUrl } = data[0];
            const res = await uploadToServer({ preUrl, file, onProgress });
            Object.assign(file, { keyName });

            onSuccess(res, file);
        } else {
            onError(new Error(msg));
        }
    };

    // 删除文件
    const removeUpload = file => {
        const files = fileList.filter(v => v.uid !== file.uid);
        setFileList([...files]);
        setCount(count - 1 > 0 ? count - 1 : 0);
        if (onChange) {
            onChange([...files]);
        }
        return true;
    };

    const changeUpload = ({ file, fileList: filelist }) => {
        if (!file.status) return;
        if (file.status === 'done') {
            setCount(count + 1);
        }
        const arr = [...filelist].slice(0, maxCount);
        setFileList([...arr]);
    };

    useEffect(() => {
        if (onChange && fileList && count === fileList?.length) {
            // 输出上传的文件信息
            if (isOutFileData) {
                onChange(fileList);
            } else {
                // 输出处理后的信息
                const arr = [];
                fileList.forEach(item => {
                    const { uid, name, url = '', originFileObj = {} } = item;
                    const { keyName = '' } = originFileObj;
                    arr.push({ uid, name, url, keyName });
                });
                onChange(arr);
            }
        }
    }, [fileList, isOutFileData, count]);

    const Component = dragAble ? Upload.Dragger : Upload;
    const uploadButton =
        listType === 'picture-card' ? (
            <div>
                <Icon style={{ fontSize: '28px' }} type="plus" />
            </div>
        ) : (
            <>
                {children || (
                    <Button>
                        <Icon type="upload" /> 点击上传
                    </Button>
                )}
                <Text type="secondary" style={{ marginLeft: 16, fontSize: 12 }}>
                    {help || `最多上传${maxCount}个文件`}
                </Text>
            </>
        );

    return (
        <>
            <Component
                ref={ref}
                name="file"
                fileList={fileList}
                beforeUpload={checkUpload}
                customRequest={customRequest}
                onRemove={removeUpload}
                onChange={(file, filelist) => {
                    changeUpload(file, filelist);
                }}
                onPreview={file => {
                    if (getFileType(file.name) === 'image' || listType === 'picture-card') {
                        handlePreview(file);
                    } else if (file.url) {
                        window.open(file.url, file.name);
                    }
                }}
                className={styles.ossUploadWrap}
                listType={listType}
                {...rest}
            >
                {fileList?.length >= maxCount ? null : uploadButton}
            </Component>
            <div
                className={styles.describe}
                // eslint-disable-next-line react/no-danger
                dangerouslySetInnerHTML={{
                    __html: describe && describe.replace(/(\r\n|\n|\r)/gm, '<br />')
                }}
            ></div>
            <Modal visible={previewVisible} footer={null} onCancel={handlePreviewCancel}>
                <img alt="example" style={{ width: '100%' }} src={previewImage} />
            </Modal>
        </>
    );
};

export default React.forwardRef(OssUpload);
